package com.yc.dict.config;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yc.api.CommonPage;
import com.yc.api.CommonResult;
import com.yc.dict.annotation.DataDict;
import com.yc.dict.annotation.EnumDict;
import com.yc.dict.annotation.ResponseDict;
import com.yc.dict.annotation.UrlDict;
import com.yc.dict.entity.DictDetail;
import com.yc.dict.service.IDictDetailService;
import com.yc.dict.vo.DictDetailVo;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;
import java.util.Objects;

@RestControllerAdvice
@RequiredArgsConstructor
public class ResponseBodyConfig implements ResponseBodyAdvice<Object> {

    @Value("${file.domain}")
    private String domain;
    private final ObjectMapper objectMapper;
    private final IDictDetailService dictDetailService;

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        ResponseDict dictData = Objects.requireNonNull(returnType.getMethod()).getDeclaringClass().getAnnotation(ResponseDict.class);
        if (dictData == null) {
            return returnType.hasMethodAnnotation(ResponseDict.class);
        } else {
            return true;
        }
    }

    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object object, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (object instanceof CommonResult<?> commonResult) {
            long code = commonResult.getCode();
            Object data = commonResult.getData();
            if (code == 200 && ObjectUtil.isNotNull(data)) {
                if (data instanceof CommonPage) {
                    List<?> list = ((CommonPage<?>) data).getList();
                    if (CollectionUtil.isEmpty(list)) {
                        return object;
                    }
                    String json = objectMapper.writeValueAsString(list);
                    JSONArray jsonArray = JSONArray.parseArray(json);
                    for (int i = 0; i < list.size(); i++) {
                        JSONObject jsonObject = jsonArray.getJSONObject(i);
                        transformDict(list.get(i), jsonObject);
                    }
                    ReflectUtil.setFieldValue(data, "list", jsonArray);
                } else if (data instanceof List<?> list) {
                    if (CollectionUtil.isEmpty(list)) {
                        return object;
                    }
                    String json = objectMapper.writeValueAsString(list);
                    JSONArray jsonArray = JSONArray.parseArray(json);
                    for (int i = 0; i < list.size(); i++) {
                        JSONObject jsonObject = jsonArray.getJSONObject(i);
                        transformDict(list.get(i), jsonObject);
                    }
                    ReflectUtil.setFieldValue(object, "data", jsonArray);
                } else {
                    if (isObject(data)) {
                        String json = objectMapper.writeValueAsString(data);
                        JSONObject jsonObject = JSONObject.parseObject(json);
                        transformDict(data, jsonObject);
                        ReflectUtil.setFieldValue(object, "data", jsonObject);
                    }
                }
            }
        }
        return object;
    }

    boolean isObject(Object o) {
        if (o instanceof Integer) {
            return false;
        } else if (o instanceof String) {
            return false;
        } else if (o instanceof Double) {
            return false;
        } else if (o instanceof Float) {
            return false;
        } else if (o instanceof Long) {
            return false;
        } else if (o instanceof Boolean) {
            return false;
        } else if (o instanceof Date) {
            return false;
        }
        return true;
    }

    /**
     * 转化数据
     */
    @SneakyThrows
    void transformDict(Object o, JSONObject jsonObject) {
        Field[] fields = ReflectUtil.getFields(o.getClass());
        for (Field f : fields) {
            Object value = ReflectUtil.getFieldValue(o, f);
            if (ObjectUtil.isNull(value)) continue;
            if (value instanceof List<?> list) {
                JSONArray jsonArray = jsonObject.getJSONArray(f.getName());
                if (ObjectUtil.isNull(jsonArray)) continue;
                for (int i = 0; i < list.size(); i++) {
                    try {
                        transformDict(list.get(i), jsonArray.getJSONObject(i));
                    } catch (Exception ignored) {
                    }
                }
            } else if (value.getClass().isArray()) {
                Object[] list = (Object[]) value;
                JSONArray jsonArray = jsonObject.getJSONArray(f.getName());
                if (ObjectUtil.isNull(jsonArray)) continue;
                for (int i = 0; i < list.length; i++) {
                    try {
                        transformDict(list[i], jsonArray.getJSONObject(i));
                    } catch (Exception ignored) {
                    }
                }
            } else {
                if (f.isAnnotationPresent(DataDict.class)) {
                    DataDict dataDict = f.getAnnotation(DataDict.class);
                    String name = dataDict.name();
                    String writeValue = dataDict.defaultValue();
                    List<DictDetailVo> dictDetailVoList = dictDetailService.queryByDictName(name);
                    if (CollectionUtil.isNotEmpty(dictDetailVoList)) {
                        writeValue = dictDetailVoList.stream()
                                .filter(e -> e.getValue().equals(value.toString()))
                                .map(DictDetail::getLabel)
                                .findFirst()
                                .orElse(writeValue);
                    }
                    jsonObject.put(f.getName() + "Text", writeValue);
                } else if (f.isAnnotationPresent(UrlDict.class)) {
                    if (StringUtils.isNotBlank(value.toString())) {
                        jsonObject.put(f.getName(), domain + value);
                    }
                } else if (f.isAnnotationPresent(EnumDict.class)) {
                    Method getDesc = value.getClass().getMethod("getDesc");
                    jsonObject.put(f.getName() + "Text", getDesc.invoke(value).toString());
                }
            }
        }
    }

}
